/*
 * Copyright (C) 2017
 *
 * Brick Yang <printfxxx@163.com>
 *
 * This program is free software. You can redistribute it and/or
 * modify it as you like.
 */

/**
 * @file	pmu_arm64.h
 * @brief	APIs get PMU counter
 */

#ifndef _PMU_ARM64_H_
#define _PMU_ARM64_H_

#ifdef __KERNEL__

#include <linux/init.h>
#include <linux/kernel.h>

#include <asm/sysreg.h>

#else	/* __KERNEL__ */

#include <inttypes.h>

#ifndef __stringify
#define __stringify_1(...)	#__VA_ARGS__
#define __stringify(...)	__stringify_1(__VA_ARGS__)
#endif

#define read_sysreg(r) ({					\
	uint64_t __val;						\
	asm volatile("mrs %0, " __stringify(r) : "=r" (__val));	\
	__val;							\
})

#ifndef unlikely
#define unlikely(x)	__builtin_expect(!!(x), 0)
#endif

#define pmu_evcnt_read(n)					\
static inline uint32_t pmu_evcnt##n##_read(void)		\
{								\
	return read_sysreg(pmevcntr##n##_el0);			\
}

#define pmu_evcnt_chain_read(n1, n2)				\
static inline uint64_t pmu_evcnt##n1##_##n2##_read(void)	\
{								\
	uint32_t hi, lo, chk;					\
	do {							\
		hi = read_sysreg(pmevcntr##n2##_el0);		\
		lo = read_sysreg(pmevcntr##n1##_el0);		\
		chk = read_sysreg(pmevcntr##n2##_el0);		\
	} while (unlikely(hi != chk));				\
	return ((uint64_t)hi << 32) | lo;			\
}

pmu_evcnt_read(0)
pmu_evcnt_read(1)
pmu_evcnt_read(2)
pmu_evcnt_read(3)
pmu_evcnt_read(4)
pmu_evcnt_read(5)
pmu_evcnt_read(6)
pmu_evcnt_read(7)
pmu_evcnt_read(8)
pmu_evcnt_read(9)
pmu_evcnt_read(10)
pmu_evcnt_read(11)
pmu_evcnt_read(12)
pmu_evcnt_read(13)
pmu_evcnt_read(14)
pmu_evcnt_read(15)
pmu_evcnt_read(16)
pmu_evcnt_read(17)
pmu_evcnt_read(18)
pmu_evcnt_read(19)
pmu_evcnt_read(20)
pmu_evcnt_read(21)
pmu_evcnt_read(22)
pmu_evcnt_read(23)
pmu_evcnt_read(24)
pmu_evcnt_read(25)
pmu_evcnt_read(26)
pmu_evcnt_read(27)
pmu_evcnt_read(28)
pmu_evcnt_read(29)
pmu_evcnt_read(30)

pmu_evcnt_chain_read(0, 1)
pmu_evcnt_chain_read(2, 3)
pmu_evcnt_chain_read(4, 5)
pmu_evcnt_chain_read(6, 7)
pmu_evcnt_chain_read(8, 9)
pmu_evcnt_chain_read(10, 11)
pmu_evcnt_chain_read(12, 13)
pmu_evcnt_chain_read(14, 15)
pmu_evcnt_chain_read(16, 17)
pmu_evcnt_chain_read(18, 19)
pmu_evcnt_chain_read(20, 21)
pmu_evcnt_chain_read(22, 23)
pmu_evcnt_chain_read(24, 25)
pmu_evcnt_chain_read(26, 27)
pmu_evcnt_chain_read(28, 29)

static inline uint64_t pmu_ccnt_read(void)
{
	return read_sysreg(pmccntr_el0);
}

#endif	/* __KERNEL__ */

#endif	/* _PMU_ARM64_H_ */
